/*
 * Copyright 2011 Christoph Ludwig (christoph[at]chludwig.de). All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.chludwig.ehumanities.provenance.background;

import java.io.File;
import java.text.MessageFormat;
import java.util.Date;
import java.util.UUID;

import javax.servlet.ServletContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.chludwig.ehumanities.provenance.entities.Archive;
import de.chludwig.ehumanities.provenance.entities.WorkflowContainer;
import de.chludwig.ehumanities.provenance.persistence.StorageArea;
import de.chludwig.ehumanities.provenance.utils.FileUtil;
import de.chludwig.ehumanities.provenance.utils.ProvenanceContextUtil;

/**
 * 
 */
public class SweeperTask implements Runnable {

  /**
   * Key of the maximum data age init parameter set in the web.xml file.
   */
  public static final String DE_CHLUDWIG_EHUMANITIES_PROVENANCE_MAX_AGE_SECONDS = "de.chludwig.ehumanities.provenance.maxageseconds";

  /**
   * Default value of the maximum data age init parameter.
   * 
   * If the maximum data age is not specified in the servlet context or if the
   * parameter cannot be parsed as a positive integer, then all data older than
   * 2 hours will periodically be discarded.
   */
  public static final long DEFAULT_MAX_AGE_SECONDS = 7200;

  private static final String MSG_NON_POSITIVE_AGE_ERROR = MessageFormat.format(
          "Servlet configuration error: Init parameter {0} must be a positive integer; fallback is {1} seconds",
          DE_CHLUDWIG_EHUMANITIES_PROVENANCE_MAX_AGE_SECONDS, DEFAULT_MAX_AGE_SECONDS);

  private static final String MSG_AGE_PARSE_ERROR = MessageFormat
          .format("Servlet configuration error: Init parameter {0} cannot be parsed as a decimal integer; fallback is {1} seconds",
                  DE_CHLUDWIG_EHUMANITIES_PROVENANCE_MAX_AGE_SECONDS, DEFAULT_MAX_AGE_SECONDS);

  private static final String MSG_DEFAULT_AGE = MessageFormat.format(
          "Servlet configuration: Init parameter {0} not set; fallback is {1} seconds",
          DE_CHLUDWIG_EHUMANITIES_PROVENANCE_MAX_AGE_SECONDS, DEFAULT_MAX_AGE_SECONDS);

  private static final String MSG_UNEXPECTED_WORKFLOW_CONTAINER_NAME = "unexpected folder {} while sweeping stale workflow containers";

  private static final String MSG_UNEXPECTED_ARCHIVE_NAME = "unexpected folder name {} while sweeping stale archives";
  
  private static final String MSG_START_SWEEP = "start purging all containers and archives not modified since {}";

  private static final String MSG_FINISHED_SWEEP = "finished purging all containers and archives not modified since {}";

  private static final Logger LOG = LoggerFactory.getLogger(SweeperTask.class);

  private ServletContext ctx;
  
  private StorageArea storageArea;

  public SweeperTask(ServletContext context) {
    this.ctx = context;
    this.storageArea = new StorageArea();
  }

  /**
   * @return the servlet context
   */
  private ServletContext getContext() {
    return ctx;
  }

  /**
   * @return the storageArea
   */
  private StorageArea getStorageArea() {
    return storageArea;
  }

  /*
   * (non-Javadoc)
   * 
   * @see java.lang.Runnable#run()
   */
  @Override
  public void run() {
    long currentTimestamp = new Date().getTime();
    Date cutoffTimestamp = new Date(currentTimestamp - getMaxAgeInMilliseconds());

    LOG.info(MSG_START_SWEEP, cutoffTimestamp);
    sweepWorkflowContainers(cutoffTimestamp);
    sweepArchives(cutoffTimestamp);
    LOG.info(MSG_FINISHED_SWEEP, cutoffTimestamp);
  }

  private void sweepWorkflowContainers(Date cutoffTimestamp) {
    File stagingFolder = getStorageArea().getStagingFolder();
    for (File containerFolder : FileUtil.getAllDirectories(stagingFolder)) {
      try {
        UUID containerId = UUID.fromString(containerFolder.getName());
        WorkflowContainer container = WorkflowContainer.containerWithId(containerId);
        container.sweepIfStale(cutoffTimestamp);
      }
      catch (IllegalArgumentException ex) {
        String unexpectedPath = containerFolder.getAbsolutePath();
        LOG.error(MSG_UNEXPECTED_WORKFLOW_CONTAINER_NAME, unexpectedPath, ex);
      }
    }
  }

  private void sweepArchives(Date cutoffTimestamp) {
    File archivesFolder = getStorageArea().getArchivesFolder();
    for (File archiveFolder : FileUtil.getAllDirectories(archivesFolder)) {
      try {
        UUID archiveId = UUID.fromString(archiveFolder.getName());
        Archive archive = Archive.archiveWithId(archiveId);
        archive.sweepIfStale(cutoffTimestamp);
      }
      catch (IllegalArgumentException ex) {
        String unexpectedPath = archiveFolder.getAbsolutePath();
        LOG.error(MSG_UNEXPECTED_ARCHIVE_NAME, unexpectedPath, ex);
      }
    }
  }

  /**
   * The maximum time the provenance tool guarantees to keep submitted data. 
   */
  private long getMaxAgeInMilliseconds() {
    String maxAgeParam = getContext().getInitParameter(DE_CHLUDWIG_EHUMANITIES_PROVENANCE_MAX_AGE_SECONDS);
    if (maxAgeParam != null) {
      try {
        long maxAgeValue = Long.parseLong(maxAgeParam);
        if (maxAgeValue <= 0) {
          LOG.error(MSG_NON_POSITIVE_AGE_ERROR);
        }
        else {
          return 1000 * maxAgeValue;
        }
      }
      catch (NumberFormatException ex) {
        LOG.error(MSG_AGE_PARSE_ERROR, ex);
      }
    }
    else {
      LOG.info(MSG_DEFAULT_AGE);
    }
    return 1000 * DEFAULT_MAX_AGE_SECONDS;
  }

}
